Skip to content

Conversation

@johnzhou721
Copy link
Contributor

@johnzhou721 johnzhou721 commented Sep 18, 2025

WIP: Fixes #1142

PR Checklist:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

@johnzhou721 johnzhou721 changed the title Testbed and core changes to support Qt backend operations New Qt Backend Sep 18, 2025
@johnzhou721 johnzhou721 changed the title New Qt Backend New Backend - Qt Sep 18, 2025
@johnzhou721
Copy link
Contributor Author

@freakboy3742 By now I have not added CI testing yet because the test skips have not yet convereged at 100%

This is rough, I need to go back and clean up comments (especially some random jokes I've made -- if you have spare time I give you permission to try to spot them) but a few things I need confirmation on:

  • Is using linux_qt as the platform identification name okay with you? It detects this platform when TOGA_QT is set to 1 or if the application is running under KDE now.
  • I've mentioned the PySide6 import hack before -- specifically doing this:
import site
import sys


def import_pyside6():
    """Temporarily break isolation to import system PySide6."""
    system_site = site.getsitepackages()
    print(system_site)
    old_path = sys.path.copy()
    sys.path.extend(system_site)
    import PySide6  # noqa

    sys.path = old_path


import_pyside6()

So if beeware/briefcase#2480 (which I've filed for a feature to replace installed PySide6 with system symlink when packaging an app) doesn't get resolved in the duration of this PR, should I still remove this hack such that the new backend will not be able to look native and access system themes?

  • I've added a NativeIcon class to icons as seen at
    class NativeIcon:
    """
    For internal use for Qt backend only
    """
    def __init__(self, native): # pragma: no cover
    self.factory = get_platform_factory()
    self._impl = self.factory.NativeIcon(native)
    since native icons are needed for undo and redo actions in Qt -- see
    icon=NativeIcon(QIcon.fromTheme("edit-undo")),
    (it demands an interfaced icon there). Is this acceptable, and if not, how should I deal with this situation?

Thank you!

johnzhou721

This comment was marked as duplicate.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 Could you please respond to those questions I've asked about above? Thank you!

@freakboy3742
Copy link
Member

@freakboy3742 By now I have not added CI testing yet because the test skips have not yet convereged at 100%

This is rough, I need to go back and clean up comments (especially some random jokes I've made -- if you have spare time I give you permission to try to spot them) but a few things I need confirmation on:

  • Is using linux_qt as the platform identification name okay with you? It detects this platform when TOGA_QT is set to 1 or if the application is running under KDE now.

No. Platform identification is only used because sys.platform isn't a reliable identifier on Android (and, a long time ago, wasn't a reliable identifier on web). Once Python 3.13 is the oldest supported release, toga.platform.current_platform should be deprecated.

You'll also note that the current GTK backend doesn't describe its platform as GTK - it's linux.

The approach used by Textual is the model to follow here. If you're on Linux, toga_qt is a candidate backend; if that's the only backend, it's used; if there's more than one candidate installed in the current environment, TOGA_BACKEND=toga_qt disambiguates.

  • I've mentioned the PySide6 import hack before -- specifically doing this:
    ...
    So if beeware/briefcase#2480 (which I've filed for a feature to replace installed PySide6 with system symlink when packaging an app) doesn't get resolved in the duration of this PR, should I still remove this hack such that the new backend will not be able to look native and access system themes?

For now, I'd say yes. We can only cook with the ingredients we're given. If Qt doesn't work in virtual environments, that's a problem for Qt to resolve. It's not up to us to work around it - if only because there may be use cases for not adopting the global environment.

If there's a hack that could be installed, I'd argue the system_pyside6 approach I've described previously is the way to install that hack.

  • I've added a NativeIcon class to icons as seen at
    class NativeIcon:
    """
    For internal use for Qt backend only
    """
    def __init__(self, native): # pragma: no cover
    self.factory = get_platform_factory()
    self._impl = self.factory.NativeIcon(native)

    since native icons are needed for undo and redo actions in Qt -- see
    icon=NativeIcon(QIcon.fromTheme("edit-undo")),

    (it demands an interfaced icon there). Is this acceptable, and if not, how should I deal with this situation?

In core - No - or, at least, there's a much bigger concept that needs to be wrapped here. If the underlying question is about "system icons" - GTK has an analogous concept, which we don't currently handle. See #2441 for some initial thoughts about this (although it doesn't mention the GTK analog that exists).

@johnzhou721
Copy link
Contributor Author

johnzhou721 commented Sep 26, 2025

@freakboy3742 Thanks for the responses, there's much going on and I'm in the middle of a major comments cleanup (it's done through GitHub's review interface so expect a huge email next week with all the ntoes I made for myself, sorry about that)

Re to response 1: Then how the heck am I gonna get all the tests skipped by platform? Do I need a bunch of stub probes to just say "skip on Qt", sort of like GTK4?

Anyways if there's 1 platform only also for Briefcase how do we handle the testbed then? Right now I used tests_backend_qt for qt probes and hacks it at conftest.py: https://github.com/beeware/toga/pull/3769/files#diff-b845936988736dd9f884ab2aeb4175fa8ee7914217ed87d3c8aad18f803e74f0R24-R33 -- I can check the backend there [b]ut (EDITED TYPO) it just feels messy to have a hack like that at all.

Re to response 2: IMO system-pyside6 for symlinking system packages is not feasible. With all this wheel madness going on you simply don't know where the final system-pyside6 package even gets installed and you can't put symlinks in wheels either (it extracts as plain text when extracted by pip, which uses its own extraction logic). There's some options for "build destination" in setup.py but that's to like an intermediate location. Should I extract this sys.modules hackery into a system-pyside6 package that installs a shim PySide6.py to replace itself, then?

Re to response 3: Yeah, but it'd look really weird on Qt without like a native icon attached to the actions. KDE inserts extra space before an action with no icon to fill up for the icon space.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 Also do you think that beeware/briefcase#2480 is a candicate issue to be handled on Briefcase? Or are we like if PySide6 doesn't work it doesn't work and we're not going to have our packaging tool symlink a system copy?

johnzhou721

This comment was marked as resolved.

johnzhou721

This comment was marked as duplicate.

@johnzhou721
Copy link
Contributor Author

johnzhou721 commented Oct 22, 2025

@freakboy3742 I've reworked the architechture to be much simpler to make it block the next 100ms whenever a window state change occurs (or is requested, in case there is another request between the request and the signal back from Qt). If in the 100ms block someone requests another state change from the programatic side, we buffer it and at the end of the 100ms, check for if the program requested a state change (NOT if the requested state change matches the current state), then we reapply. Multiple change events from the user in rapid succession will keep blocking until 100ms from the last one.

I'll wait for test pass, and rerequest a review. All items should be resolved.


EDIT -- To clarify: this will no longer handle rapid changes correctly if the user mashes keys or presses MINIMIZE in the middle of the changes, but you said in the Qt issue that this is not a major concern (as opposed to an infinite-loop architechutre).

@johnzhou721 johnzhou721 requested a review from kattni October 22, 2025 01:03
@freakboy3742
Copy link
Member

@johnzhou721 Ack - I'll take a look as soon as I can; early next week seems most likely.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 FYI -- While updating the support table, I also corrected some errors in it -- they should be fairly obvious.

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting close enough that it's worth merging (if only because reviewing this PR is like swimming through molasses, and we need smaller chunks to review :-) )

I've flagged a few issues about removing the system backend for now; there's a few considerations there, so for a first pass, let's acknowledge but avoid the problem.

There's a couple of other minor docs issues that I've flagged - the biggest is to actually split the Linux docs into two pages.


On Linux and other Unix-like operating systems, Toga currently provides 2 backends: GTK and Qt. The GTK 3 backend is mostly complete, but the Qt backend is currently a pre-alpha prototype.

## GTK Backend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The heading depth tweaks that have been required here leads me to think that we should probably break this into a GTK page and a Qt page, with a Linux page that provides the overall context and links to the two options.

That almost means the handful of links to linux.md that assume it's a GTK page (such as the GTK README) will also need updating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@freakboy3742 I've only found in the GTK README with /linux in it; let me know of other places if you find any (I used git grep on this repo.)

Comment on lines 140 to 141
"../qt",
"PySide6-Essentials==6.10.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"../qt",
"PySide6-Essentials==6.10.0",
"../qt[pyside6]",

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@freakboy3742 Ok... I remember the reason why I wrote it the previous way I did now that I've (mindlessly) pushed your change and the CI failed. It's because this error gets raised:

Unable to find local requirement ../qt[pyside6]

It seems that there's no handling of extras in local requirements in Briefcase's Linux integration. Is adding extras integration there mandatory for this to proceed? Or is this old way acceptable? Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok - this surprised me, but it's definitely happening, and I think I understand why. Logged as beeware/briefcase#2560.

In the meantime, the old approach seems the best way forward.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 I've addressed all your comments, and added 4 in the inline conversations. Please review again and let me know of anything that needs changing. Thank you!

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made one last tweak to the documentation - using an interstitial Linux landing page, rather than have completely separate documents for GTK and Qt. This is for two reasons - firstly, because users will more likely think of themselves as Linux users than GTK users; and secondly, because any existing link to reference/platforms/linux should continue to work.

But with that change, and a couple of other minor tweaks, this is definitely at the point where incremental improvement will be more helpful than trying to review this monster PR - so I think this is good to land!

Thanks for all your work on this! Now we just need to fill out all the other widgets... 🤣

Comment on lines 140 to 141
"../qt",
"PySide6-Essentials==6.10.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok - this surprised me, but it's definitely happening, and I think I understand why. Logged as beeware/briefcase#2560.

In the meantime, the old approach seems the best way forward.

@freakboy3742 freakboy3742 merged commit f444a83 into beeware:main Nov 12, 2025
56 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New backend - Qt

3 participants